React Router 原理
React Router 是 React 官方提供的路由管理器,它可以帮助我们管理应用的路由,实现不同 URL 对应的不同组件的渲染。
工作原理
我们先看一段 React Router 的代码:
jsx
import React from'react';
import { BrowserRouter as Router, Switch, Route } from'react-router-dom';
function App() {
return (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/users" component={Users} />
<Route path="/users/:id" component={User} />
<Route path="/users/:id/edit" component={UserEdit} />
</Switch>
</Router>
);
}
export default App;
React Router 的工作原理如下:
- 首先,我们需要定义好路由规则,比如我们有如下路由规则:
/users
/users/:id
/users/:id/edit
然后,我们需要在应用的入口文件中,使用 React Router 的
Router
组件,将路由规则和对应的组件进行绑定。当用户访问
/users
页面时,React Router 会匹配到对应的路由规则,并渲染Users
组件。当用户访问
/users/:id
页面时,React Router 会匹配到对应的路由规则,并渲染User
组件,并将:id
作为参数传递给User
组件。当用户访问
/users/:id/edit
页面时,React Router 会匹配到对应的路由规则,并渲染UserEdit
组件,并将:id
作为参数传递给UserEdit
组件。
React Router 的路由匹配规则是基于路径的,因此,我们需要确保路径的正确性。
- 当用户访问
/users/123/edit
页面时,React Router 会匹配到/users/:id/edit
路由规则,并渲染UserEdit
组件,并将123
作为参数传递给UserEdit
组件。
代码实现
1. Router 组件
Router
组件是 React Router 的核心组件,它负责管理应用的路由,并渲染对应的组件。
jsx
export default class BrowserRouter extends React.Component {
constructor(props) {
this.history = createBrowserHistory();
}
render() {
const
return (
<RouterContext.Provider value={{history: this.history, location: this.history.location, staticContext: undefined}}>
{this.props.children}
</RouterContext.Provider>
);
}
}
2.Route 组件
jsx
export default class Route extends React.Component {
constructor(props) {
super(props);
this.state = {
match: props.computeMatch(props.location.pathname)
};
}
componentDidMount() {
const { history } = this.props;
const unlisten = history.listen((location) => {
this.setState({
match: this.props.computeMatch(location.pathname)
});
});
this.setState({
unlisten
});
}
componentWillUnmount() {
this.state.unlisten();
}
render() {
const { component: Component, render, children,computedMatch } = this.props;
if (!computedMatch) {
return null;
}
const match = computedMatch|| matchPath(this.props.location.pathname, this.props);
if (match) {
if (Component) {
return <Component {...this.props} />;
} else if (render) {
return render({...this.props, match });
} else if (children) {
return children({...this.props, match });
}
}
return null;
}
}
3.Switch 组件
jsx
export default class Switch extends React.Component {
render() {
const { location, children } = this.props;
let match = null;
React.Children.forEach(children, (element) => {
// 如果没有 match 才执行计算
if (match == null && React.isValidElement(element)) {
const { path, exact, strict, sensitive, from } = element.props;
const pathname = getFullPath(location.pathname, from);
if (path) {
match = matchPath(pathname, { path, exact, strict, sensitive });
} else {
match = pathMatch(pathname);
}
}
});
return match ? React.cloneElement(React.Children.only(children), { computedMatch: match }) : null;
}
}
总结
Router 组件是负责上下文的注入,还有统一前缀等配置,Route 组件可以根据 path 进行匹配,并且内部提供了 compuMatch 方法,由外部控制是否渲染,Switch 组件可以根据路由匹配到的第一个组件进行渲染。